| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050 |
- /**
- * 播放过程中必要的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,
- formatBeatUnit,
- } 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'
- import { unitTestData } from '/src/subpages/colexiu/unitTest'
- import { modelType } from '/src/subpages/colexiu/buttons'
- import { metronomeData } from '/src/helpers/metronome'
- 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
- const beatUnit = duration.beatUnit || 'quarter'
- if (detailState.repeatedBeats) {
- // 音频制作问题仅2拍不重复
- numerator = numerator === 2 ? 4 : numerator
- }
- //酷乐秀计算方法
- // const time = !detailState.needTick && !detailState.skipTick ? ((denominator * 60) / speed / denominator) * numerator : 0
- // 管乐迷计算方法
- // console.log('diff', speed, duration, formatBeatUnit(beatUnit), denominator, numerator, (numerator / denominator))
- // console.log('👀时间')
- const time = !detailState.needTick && !detailState.skipTick ? (60 / speed * formatBeatUnit(beatUnit)) * (numerator / denominator) : 0
- // console.log({duration, t:(60 / speed * formatBeatUnit(beatUnit)) * (numerator / denominator), time, numerator,denominator, "duration.numerator": duration.numerator})
- 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)
- }
- /**播放状态改变时,向父页面发送事件 */
- export const sendParentMessage = (playState: string) => {
- window.parent.postMessage(
- {
- api: 'headerTogge',
- playState: playState
- },
- '*'
- )
- }
- watchEffect(() => {
- detailState.maskStatus = state.playState === 'play'
- if(['play', 'pause'].includes(state.playState)) {
- sendParentMessage(state.playState)
- }
- })
- 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)
- state.audiosInstance.setCurrentTime(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
- try {
- metronomeData?.metro?.sound(currentTimeNum);
- } catch (error) {}
- 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)
- try {
- metronomeData?.metro?.sound(currentTimeNum);
- } catch (error) {}
- 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) {
- 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
- 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()
- if (detailState.activeDetail?.isAppPlay) {
- pause()
- } else {
- await state.audiosInstance.pause()
- }
- // 如果是单元测验 和课后训练直接结束
- if (unitTestData.isSelectMeasureMode && state.evaluatingStatus){
- console.log(1)
- event.emit('ended')
- return
- }
- setSectionModeCurrentTime()
- clearAccelerateRefreshPlayer()
- setTimeout(() => {
- if (detailState.section.length){
- setPlayState()
- }
- }, 1000)
- state.loading = false
- return
- }
- // 当MIDI进度超过最后一个音符时间触发结束事件
- if(detailState.activeDetail?.isAppPlay && state.durationNum + 3 < currentTimeNum) {
- if (state.evaluatingStatus) {
- pause()
- console.log(2)
- 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,
- },
- })
- } 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,
- },
- })
- } else {
- state.playState = state.audiosInstance.getStatus()
- clearAccelerateRefreshPlayer()
- accelerateRefreshPlayer()
- }
- }
- 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)
- } else {
- state.playState = state.audiosInstance.getStatus()
- clearAccelerateRefreshPlayer()
- state.audiosInstance.pause()
- }
- }
- 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 = () => {
- 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)
- // 如果没有节拍器,默认提前一个小节
- // setCurrentTime(getFirsrNoteByMeasureListIndex(measureListIndex).time)
- detailState.sectionFlash = true
- } else {
- setCurrentTime(0)
- }
- }
- }
- export const setPlayerView = () => {
- if (detailState.sectionStatus) {
- syncStepIndex(getIndex(detailState.times, state.currentTimeNum))
- if (detailState.section.length === 2) {
- 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: SettingState.sett.hertz,
- },
- })
- } else {
- await promisefiyPostMessage({
- api: 'cloudSuspend',
- })
- }
- const cloudGetMediaStatused = await promisefiyPostMessage({
- api: 'cloudGetMediaStatus',
- })
- state.playState = cloudGetMediaStatused?.content.status
- console.log(cloudGetMediaStatused, 'cloudGetMediaStatused')
- }
- export const toggleState = async (delay?: number) => {
- if (modelType.value === 'init') return
- 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) => {
- // 节拍时间是固定的无需调整
- 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 () => {
- if (detailState.activeTick > -1 || state.ticking) {
- return
- }
- await syncPlayState()
- if (state.playState !== 'pause' && state.playState !== 'suspend') {
- await toggleState()
- return
- }
- 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) {}
-
- // }
-
- }
- 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 () => {
- console.log('SettingState.sett.camera:', SettingState.sett.camera, " SettingState.eva.save:", SettingState.eva.save)
- if (
- SettingState.sett.camera &&
- SettingState.eva.save
- ) {
- postMessage({
- api: 'startCapture',
- })
- }
- }
- export const endCapture = async () => {
- if (SettingState.eva.save && SettingState.sett.camera ) {
- postMessage(
- {
- api: 'endCapture',
- })
- }
- }
- export const setCaptureMode = async () => {
- if (browserInfo.isApp && SettingState.sett.camera) {
- postMessage({
- api: 'setCaptureMode',
- content: {
- mode: state.evaluatingStatus ? 'evaluating' : 'practice',
- },
- })
- }
- }
|