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

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

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

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

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

闲话少叙
这里讨论的页面是: wordsZhenjing.js
音频播放的部分逻辑在: svSupListen.js
配置弹窗组件: zhenjingConfig.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】听写模式,如果有同替,可以听写同替

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

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

用字段为空这么判断可能不太优雅,先这样吧。
实现这个需求的时候遇到了很多问题,卡了很久,因为播放逻辑的控制,使用了很多变量,这些变量的值在很多地方被修改,debug不好跟踪。最初的需求跟现在的需求不同,原来的变量不能满足现在的需求,需要增加新的变量。修改旧的变量又可能造成原有功能bug, 纠结如何重构。我还不会重构

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


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,
        })
      }
    }
  }
}

需求2遇到的问题是听写同替的时候,也播放了例句的音频。解决这个bug,可以判断一下当前tag是否是 同替,如果是同替才播放,其他的tag类型不播放:

        // 如果下一个是同替才播, 其他都不播, 下标++ 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
          })
        }

遇到的问题:

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

第一个问题,注意isEnd: true, audioFinish函数, 组间切换的逻辑:

        if (this.data.repeatGroup == 1 && !this.data.shouldPlayExpand) {
          this.nextGroupRequest()
        } else {
          this.playExpandArr()
          this.setData({
            shouldPlayExpand: false
          })
        }
      } else {
        if (this.data.repeatGroup == 1) {
          //听下一组 切换url
          this.nextGroupRequest()
        } else {
          //循环这一组
          this.controlPlayTap()
        }
      }

svSupListen.js这个文件有700多行, 有很多重复代码,可以考虑优化 比如switch case写了三遍,60行,可以抽取
播放拓展,也使用了一个拓展音频下标expandIndex

posted on   土匪7  阅读(155)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示