06. 音频

Posted Composition55555

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了06. 音频相关的知识,希望对你有一定的参考价值。

文章目录


写在前面

  • 本文写了很多官网的API的相关知识点,可略过直接到官网查看

记第一个小程序DEMO

  • 名称:仿网易云音乐小程序
  • 接口:网易云音乐NodeJS版 API
  • 内容:主要实现轮播图、歌单推荐、排行榜、歌单显示、歌曲播放、歌词显示这几部分功能,其它暂未作考虑

  • 本文主要内容:实现歌曲播放、歌词显示

歌曲播放设计到音频,所以我们先来看看如何实现小程序的音频


01. Audio

  • 可以使用Audio组件进行音频的播放

    一开始走远了,只在组件中找到这个,然后就开始做,后来才发现已废弃,😢


02. innerAudioContext对象

详见官网音频

// 创建实例
const innerAudioContext = wx.createInnerAudioContext()

(1)属性

  • src:设置音频资源地址

  • startTime:设置开始播放的时间,单位为s

  • autoplay:设置是否自动播放

  • loop:设置是否循环播放

  • obeyMuteSwitch:设置是否遵循系统静音开关

  • volume:设置音量

  • playbackRate:设置播放速度


  • duration:获取当前音频长度

  • currentTime:获取当前音频播放位置,单位为s

  • paused:获取当前是否是暂停状态

  • buffered:获取当前音频缓冲时间点

(2)方法

  • play():播放

  • paused():暂停

  • stop():停止,停止后再播放会从头开始

  • seek(number):跳转歌曲播放位置,单位为s

  • destory():销毁当前实例

  • onCanplay()offCanplay():监听音频进入可以播放状态的事件、取消监听

  • onPlay()offPlay():监听音频播放事件、取消监听

  • onPause()offPause():监听音频暂停事件、取消监听

  • onStop()offStop():监听音频停止事件、取消监听

  • onEnded()offEnded():监听音频自然播放结束事件、取消监听

  • onTimeUpdate()offTimeUpdate():监听音频播放进度更新事件、取消监听

  • onError()offError():监听音频播放错误事件

  • onWaiting()offWaiting():监听音频加载中事件

  • onSeeking()offSeeking():监听音频进行跳转操作事件

  • onSeeked()offSeeked():监听音频完成跳转操作事件


03. backgroundAudioManager对象

详见官网背景音频

// 创建实例
const backgroundAudioManager = wx.getBackgroundAudioManager()

(1)属性

  • 大部分同innerAudioContext
  • srcbackgroundAudioManager设置了新的音频资源地址时,会自动开始播放音频

  • title:设置音频标题,必填项

  • epnaem:专辑名

  • singer:歌手名

  • coverImgUrl:封面图

  • webUrl:分享功能

    背景音频播放时,会在状态栏显示一个原生音频播放器

    以上内容都是为播放器设置的


  • protocol:音频协议

(2)方法

  • 只有监听事件,没有取消监听事件,方法同innerAudioContext
  • onNext():下一曲,限ios
  • onPrev():上衣区,限IOS

04. 实现歌曲播放

这里为了方便查看,统一将代码放在一起

变量说明:

musicUrl:歌曲播放地址

playState:播放状态标识

playStatus:控制图片选择.可选值 paused | running

/* 以backgroundAudioManager为例 */
// 1.创建实例
const backgroundAudioManager = wx.getBackgroundAudioManager()

// 2. 设置资源地址(背景音频会自动播放)
backgroundAudioManager.src = this.data.musicUrl
// 设置标题和图片——原生播放器,与页面不相关
backgroundAudioManager.title = this.data.musicName
backgroundAudioManager.coverImgUrl = this.data.musicPic

// 3.播放事件
handleMusicPlay() 
    // 歌曲播放
    if (this.data.playState) 
        backgroundAudioManager.play()
     else 
        backgroundAudioManager.pause()
    

// 4.监听歌曲播放状态
handleMusicWatch() 
    // 4.1 监听歌曲播放状态
    backgroundAudioManager.onPlay(() => 
        this.handlePlay(false)
    )
    // 4.2 监听歌曲暂停状态
    backgroundAudioManager.onPause(() => 
        this.handlePlay(true)
    )
    // 4.3 监听歌曲自然播放结束事件
    backgroundAudioManager.onEnded(() => 
        this.handlePlay(true)
        setTimeout(() => 
            backgroundAudioManager.play() // 再次播放
        , 2000)
    )
,
// 5. 播放状态改变事件
handlePlay(flag) 
    // 播放图标状态切换
    this.setData(
        playState: flag,
    )
    // 图片旋转状态
    this.setData(
        playStatus: flag ? 'paused' : 'running',
    )
,

播放图标?

  • 页面添加了一个播放图标,用于显示当前歌曲的播放状态

控制图片旋转?

  • CSS的动画属性animation-play-state: paused | running;
  • 用来控制动画的暂停与播放
<!-- 播放图标 -->
<view class="play-icon" bindtap="handleMusic">
    <text wx:if=" playState " class="iconfont icon-play"></text>
    <text wx:if=" !playState " class="iconfont icon-paused"></text>
</view>
<!-- 音乐图片 -->
<view class="music-pic" style="animation-play-state:  playStatus ">
    <image src=" musicPic " mode="scaleToFill" lazy-load="false"></image>
</view>
  • 效果图:


05. 实现歌词滚动

(1)处理歌词数据

  • 请求到的歌词数据格式为:"[00:00.000] 这是一句歌词\\n[00:01.000] 这是一句歌词\\n..."

  • 所以先要将其通过\\n分割为数组形式: ["[00:00.000] 这是一句歌词","[00:00.000] 这是一句歌词",...]

  • 然后要将每一个数组项分割为【时间与歌词】两部分:

    • 可以通过]符号对每一项数组元素进行分割,然后通过subString方法截取
    • 结果应该为:'00:00.000''这是一句歌词'
    • 最后处理时间,将其通过:分割为分钟数和秒数,算出总时间(s)
    • 最后的结果应该得到:时间数组、歌词数组,两者长度一致
/* 代码实现 */
// 1. 分割歌词为数组
let lyric = this.data.musicLyric.lyric.split('\\n')
let lyricTime = [] // 时间数组
let lyricText = [] // 歌词数组

// 2. 遍历:分别取出时间、歌词
lyric.forEach(item => 
    // item格式:    [00:00.00] 歌词

    // 通过 ‘]’ 符号取出:先找到 ‘]’ 所在索引
    let index = item.indexOf(']')

    // 对数组项进行遍历:当遍历项的歌词 不为空时 才取出
    if (item && item.substring(index + 1) != '') 
        // 取出歌词:substring(index + 1):表示取后面所有字符串
        lyricText.push(item.substring(index + 1))

        // 处理时间:先去除前后 [] 符号,item.substring(1, index)
        // 再根据 : 分割为数组:分钟数、秒数
        let time = item.substring(1, index).split(':')
        // 然后将 分钟数转换为秒数 最后再相加 保留三位小数 并转换为数字类型
        lyricTime.push((time[0] * 60 + time[1] * 1).toFixed(3) - 0)
    
)

this.setData(
    lyricTime,
    lyricText,
)

(2)歌词匹配显示

使用backgroundAudioManager.onTimeUpdate()实时监听歌曲进度

然后根据请求返回的——时间数据,与当前播放进度时间比较,来确定显示哪一行歌词

变量说明:

lyricTime:歌词时间数组

current:当前歌词、时间 对应的数组下标,初始值为0

handleMusicWatch()
    // 实时监听歌曲进度
    backgroundAudioManager.onTimeUpdate(() => 
        // 获取当前歌曲进度
        let currentTime = backgroundAudioManager.currentTime
        // 获取歌词对应的 时间进度
        let time = this.data.lyricTime[this.data.current + 1]
        // 如果 当前的歌曲进度 大于 歌词的进度
        // 则让歌词显示下一行,即让数组序号 current +1
        // PS:这里只能尽量让歌词跟着滚动,不是完全匹配,
        if (currentTime > time) 
            this.setData(
                current: this.data.current + 1,
            )
        
    )

<!-- 歌词显示 -->
<view class="lyric">
    <!-- 内容 -->
    <view class="content" style="transform:translateY( -current * 60 rpx);">
        <!-- 展示所有歌词,然后利用 CSS3 属性——滚动歌词、更改当前播放歌词的颜色 -->
        <!-- 注意:style属性内的语句 不能换行!!! -->
        <text
              wx:for=" lyricText "
              wx:key="index"
              class=" index == current ? 'current' : '' "
              > item </text>
    </view>
</view>
/* 当前播放歌词样式 */
.current 
    color: blue!important;
    background: -webkit-linear-gradient(left, transparent, #ffffff1f, transparent);

/* 歌词 */
.lyric 
    width: 100%;
    margin-top: 12rpx;
    height: 180rpx;
    overflow: hidden;
    text-align: center;

    .content 
        position: relative;
        margin: auto;
        padding-top: 60rpx;
        width: 100%;
        height: 100%;
        font-size: 30rpx;
        transition: transform 0.3s linear;
        text 
            display: block;
            height: 60rpx;
            line-height: 60rpx;
            font-weight: bold;
            color: #ccc;
            // background: -webkit-linear-gradient(left, transparent, #ffffff1f, transparent);
            // 控制两个属性的过渡,需要用 , 分隔
            transition: color 0.3s linear, background 0.3s linear;
        
        .bg 
            width: 100%;
            height: 60rpx;
            position: absolute;
            top: 48rpx;
            // background-color: #ffffff1f;
            background-image: -webkit-linear-gradient(left, transparent, #ffffff1f, transparent);
            border-radius: 50rpx;
            transition: transform 1s linear;
        
    

  • 实现效果


(PS:以上内容可能描述不够清晰,请谅解)
(另外,暂时停止了)

以上是关于06. 音频的主要内容,如果未能解决你的问题,请参考以下文章

如何静音或只选择一个音频通道?

如何使用 libsndfile 在音频文件中打印静音?

Sox:使音频文件的前 100 毫秒静音,然后淡入

Java 实现音频添加自定义时长静音(附代码) | Java工具类

Java 实现音频添加自定义时长静音(附代码) | Java工具类

使用 NAudio 录制音频并在麦克风输入静音时写入文件