小程序音频播放复杂流程的经验和思考

发布时间 2023-04-22 13:04:58作者: 土匪7

最近两周在写一个新的小程序项目,托福词汇真经。这个小程序的难点是音频播放流程比较复杂

之前我在雅思听力小程序里实现过雅思词汇真经的功能

前期讨论的结果是基于原有的功能开发

开发过程中碰到了一些问题,这里记录一下,同时梳理一下这里音频播放的逻辑,后面如果再增加新功能,可以快速处理

闲话少叙
这里讨论的页面是: wordsZhenjing.js
四种模式介绍

这个页面的音频播放最开始只有两个模式:1.循环听 2.听写
后来增加了在线拼写模式,这次又增加了测试模式。但是这两种模式的详情页都是新的页面,逻辑不在这里,这里不讨论
循环听VS听写 最开始使用 isListen 这个布尔值变量区分,但是后来增加了拼写和测试模式,所以现在使用modeIndex变量区分(0:测试; 1:循环听; 2:听写; 3:拼写) 。
所以isListen是否可以去掉呢? xml里面使用了这个变量,先留着吧

旧的模式介绍
听写模式: 只播放单词。播放完毕去对答案

循环听模式:播放单词,例句, 同替,过去式,过去分词。这个单词的相关信息播放完,继续播放下一个单词的例句,同替,过去式...
一组全部播放完毕,根据不同设置,可以重头播放这一组,也可以播放下一组

旧的关键逻辑
Q:循环听模式是怎么顺序播放 单词,例句,同替,单词,例句,同替...... 如此循环的呢?
A: 每个单词的例句, 同替,过去式格式化成数组 formAudios, 追加字段audioInfos, 增加一个innerIdx 字段,用于在单词的 audioInfos 数组里面顺序播放音频

/**
   * 格式化音频
   */
  formAudios(list) {
    list.forEach((item) => {
      let audioInfos = typeof (item.near) != 'undefined' ? Array.from(item.near) : []

      if (item.past) {
        audioInfos.push(...item.past)
      }

      if (item.participle) {
        audioInfos.push(...item.participle)
      }

      if (item.template && item.template.url.length > 0) {

        let tempItem = {
          tag: 'template',
          url: item.template.url,
          text: item.template.en
        }
        audioInfos.unshift(tempItem)
      }

      let wordItem = {
        tag: 'word',
        url: item.url,
        text: item.text
      }
      audioInfos.unshift(wordItem)
      item.audioInfos = audioInfos
    // console.log('检查----',audioInfos)
    })
  },

接口返回的数据格式:

输出结果:

播放逻辑:


// 1洗脑听单词,下一个音频判断逻辑
function listenToNext() {

  if (this.hasPlayGroupUrl) {
    this.hasPlayGroupUrl = false
    this.inLogic_xiNaoNext()
    this.listen2NextWord()
  } else {
    let currWordItem = this.data.currWordItem
    
    if(!currWordItem.text){ //判断是否是拓展
      this.expandStart()
      return 
    }

    // 根据innerIdx 判断是否播放到这一组的边缘
    if (this.data.innerIdx < 0) {
      console.log('item??--',this.data.currWordItem)
      if(this.data.currWordItem){ //听写同替到头,切换洗脑听,重复播放间隔音bugFix
        this.playMainUrl()
        this.setData({
          innerIdx: 0
        })
      }
    }
    else if (this.data.innerIdx < currWordItem.audioInfos.length - 1) {
      if (this.isPlayInnerGapAudio) {
        this.isPlayInnerGapAudio = false
        this.playIntervalUrl()
      } else {
        this.isPlayInnerGapAudio = true
        this.data.innerIdx++

        // 如果下一个是同替才播, 其他都不播, 下标++ todo:抽取优化
        if(this.data.dictateSynonym && this.data.modeIndex == 2){
          let nextInfor = currWordItem.audioInfos[this.data.innerIdx]
          if(nextInfor.tag == '同替'){
            this.initAudio()
            let codeUrl = encodeURI(currWordItem.audioInfos[this.data.innerIdx].url)
            this.backgroundAudioManager.src = codeUrl
            this.backgroundAudioManager.playbackRate = parseFloat(this.data.speed)
            this.backgroundAudioManager.play()
            this.setData({
              innerIdx: this.data.innerIdx
            })
          }else {
            this.setData({
              innerIdx: this.data.innerIdx
            })
            this.isPlayInnerGapAudio = false
            this.playIntervalUrl()
          }
        }else {
          this.initAudio()
          let codeUrl = encodeURI(currWordItem.audioInfos[this.data.innerIdx].url)
          this.backgroundAudioManager.src = codeUrl
          this.backgroundAudioManager.playbackRate = parseFloat(this.data.speed)
          this.backgroundAudioManager.play()
          this.setData({
            innerIdx: this.data.innerIdx
          })
        }

      }
    } else {
      if (this.isPlayInterval) {
        this.repetitionCount--
        if (this.repetitionCount <= 0) { //bugFix:播放两遍拓展
          // 播放间隔音
          // 当前没有同替,不播放间隔音
          if(currWordItem.near){
            this.listenPlayInterval()
            this.hasPlayGroupUrl = true
          } else {
            this.hasPlayGroupUrl = false
            this.inLogic_xiNaoNext()
            this.listen2NextWord()
          }

          // 如果到头了,让下标++
          if(this.data.wordIndex == this.data.wordArr.length - 1){
            
            let idx = this.data.wordIndex
            this.setData({
              wordIndex: idx++
            })
          }
        } else if (this.repetitionCount > 0) {

          this.listen2NextWord()
          this.setData({
            innerIdx: -1
          })
        }else {
          this.inLogic_xiNaoNext()
            this.listen2NextWord()
            this.setData({
                innerIdx: -1
            })
        }
      }
    }
  }
}

这次新增的需求:
【需求1】循环听模式,单词听完了,如果有拓展,播放拓展
【需求2】听写模式,如果有同替,可以听写同替

【需求1】
Q: 如何实现?
A:一组音频播放完,检查当前currWordItem是否是拓展,是拓展就播放拓展。是否是拓展的判断如下:

if(!currWordItem.text){ //判断是否是拓展
   this.expandStart()
   return 
}

用字段为空这么判断可能不太优雅,先这样吧

【需求2】
Q: 如何实现?
A: 增加字段 dictateSynonym, 在toNext函数里面判断 如果是dictateSynonym的情况,也调用dictateSynonym:


function toNext() {
  if (util.noRepeatTap(this, 100)) {
    return
  }
  if (this.data.isPlay) {

    if (this.data.isListen) {
      console.log('check idx->',this.data.wordIndex)
      this.listenToNext()
      return
    }

    // 听写同替模式
    if(this.data.dictateSynonym){
      this.listenToNext()
      return
    }

    if (this.isPlayInterval) {
      this.repetitionCount--
      if (this.repetitionCount == 0) {
        this.data.wordIndex++
        this.repetitionCount = parseInt(this.data.repetition)
      }
    }

    // 播放完成
    if (this.data.wordIndex >= this.data.wordArr.length) {
      this.setData({
        isEnd: true,
        isPlay: false,
      })
      this.playChange(this.data.isPlay)
      this.audioFinish()
    } else {
      this.data.currWordItem = this.data.wordArr[this.data.wordIndex]
      if (this.isPlayInterval) {
        var intervalCount = parseInt(this.data.interval)
        this.isPlayInterval = false
        var intervalAudioUrl = ""
        console.log('listen count->',intervalCount)
        switch (intervalCount) {
          case 1:
            intervalAudioUrl = intervalAudio1
            break;
          case 2:
            intervalAudioUrl = intervalAudio2
            break;
          case 3:
            intervalAudioUrl = intervalAudio3
            break;
          case 4:
            intervalAudioUrl = intervalAudio4
            break;
          case 5:
            intervalAudioUrl = intervalAudio5
            break;
          case 7:
            intervalAudioUrl = intervalAudio7
            break;
          case 10:
            intervalAudioUrl = intervalAudio10
            break;
          default:
            intervalAudioUrl = intervalAudio1
            break;
        }
        this.initAudio()
        this.backgroundAudioManager.playbackRate = 1.0
        this.backgroundAudioManager.src = intervalAudioUrl
        this.backgroundAudioManager.play()
      } else {
        console.log(this.data.currWordItem.url)
        this.playMainUrl()
        this.setData({
          currWordItem: this.data.currWordItem,
          wordIndex: this.data.wordIndex,
          percent: (this.data.wordIndex + 1) / this.data.wordArr.length * 100,
        })
      }
    }
  }
}

遇到的问题:
增加播放拓展的逻辑-前进后退-自动播放完切换下一个音频
svSupListen.js listenToNext 一百行逻辑
同步弹窗选中的跟保存的记录