微信小程序实现语音录制,上传,播放 - 新闻资讯 - 云南小程序开发|云南软件开发|云南网站建设-昆明葵宇信息科技有限公司

159-8711-8523

云南网建设/小程序开发/软件开发

知识

不管是网站,软件还是小程序,都要直接或间接能为您产生价值,我们在追求其视觉表现的同时,更侧重于功能的便捷,营销的便利,运营的高效,让网站成为营销工具,让软件能切实提升企业内部管理水平和效率。优秀的程序为后期升级提供便捷的支持!

您当前位置>首页 » 新闻资讯 » 小程序相关 >

微信小程序实现语音录制,上传,播放

发表时间:2021-1-5

发布人:葵宇科技

浏览次数:125

记录技术,分享技术,做个伟大的搬运工。

框架选用

开发微信小程序,使用的是unapp。 为什么要选用这个,因为他比较成熟,之前用过mpvue,kbone都是特别的不太成熟。后来经过各种选型选定了uniapp。


遇到的问题
  • 录音授权。
  • 长按录音,判断手指是否划出长按的区域
  • 文件上传。
  • 多录制语音播放。

初始化工作
  1. 定义全局录音对象和audio对象,并且格式化自己想要的音频格式
const recorderManager = uni.getRecorderManager(); // 创建录音对象
const audioContext = wx.createInnerAudioContext(); // 创建audio对象
const options = {
    duration: 600000, //指定录音的时长,单位 ms,最大为10分钟(600000),默认为1分钟(60000)
    sampleRate: 16000, //采样率
    numberOfChannels: 1, //录音通道数
    encodeBitRate: 96000, //编码码率
    format: 'mp3', //音频格式,有效值 aac/mp3 等
    frameSize: 50 //指定帧大小,单位 KB
};
复制代码

录音授权

录音收取主要从两个方面考虑

  1. 申请授权用户直接同意授权
  2. 用户拒绝授权后,打开设置,用户收到授权。
uni.authorize({
    scope: 'scope.record',
    success() {
        // 用户已经同意授权,可以进行录音
    },
    fail() {
    	// 此处用户授权失败,需要打开设置,去手动授权。  在某些情况下openSetting无法打开
        // 可以通过fail的方式,通过弹窗用户打开设置。
        uni.openSetting({
            success: res => {
            	// 没有授权的情况下,弹窗提示
                if (!res.authSetting['scope.record']) {
                    //未设置录音授权
                    uni.showModal({
                        title: '提示',
                        content: '您未授权录音,功能将无法使用',
                        showCancel: false
                    });
                } else {
                    // 第二次才成功授权
                    // 用户已经同意授权,可以进行录音
                }
            },
            fail: function() {
                uni.showModal({
                    title: '提示',
                    content: '您未授权录音,功能将无法使用',
                    showCancel: false,
                    success: function(res) {
                        uni.openSetting();
                    }
                });
            }
        });
    }
});

复制代码

官方api链接

接口调整链接


长按录音 / 滑动区域 / 停止长按

uniapp中的方法
@longpress 长按(手指触摸超过350ms)  
@longtap 长按  
@tap 点击  
@touchcancel 手指触摸被打断,如来电提醒,弹窗  
@touchend 手指触摸动作结束,如松开按钮  
@touchmove 手指触摸后移动  
@touchstart 手指触摸动作开始 
复制代码

录制操作
  1. 获取当前点击元素距离顶部的距离
  • 长按的同时,要开始计时,已经授权的时候要开始录制语音。
// 小程序中获取当前点击元素的距离和其他的有所不同,以下是获取的方法
const query = uni.createSelectorQuery().in(this);
  query
      .select('.record-button')
      .boundingClientRect(data => {
          // data 当前元素的各个信息
      })
      .exec();
复制代码
  1. 通过event获取当前各项信息
  • 滑动超出的时候要进行停止录音,并且要清空计时器。
  • 滑回来的时候要继续录音,继续定时器。
  • 此处需要加个中间状态,通过监听来进行继续录制录音和停止录制语音
let touches = e.touches[0];
  // 超出的时候
  if (touches.pageY < this.reocrdLocation.top) {
      clearInterval(this.timerInfo);
      recorderManager.pause();
  } else {
      recorderManager.resume();
  }
复制代码

停止长按
  • 判断录音时长太短的话,不进行上传,
  • 符合条件的进行上传,并且清空定时器,停止录制
recorderManager.stop();
// 监听停止事件
recorderManager.onStop(res => {
    if (this.duration < 1) {
        uni.showToast({
            title: '录制时间太短',
            duration: 1000,
            icon: 'none'
        });
        return;
    }
    
    // 符合条件的,推进数组。
   

    this.voiceList.push({
        size: res.fileSize, // 本地的进度
        progress: -1,//-1 没有上传, -100 上传失败, 100 上传成功, 0 ~ 100上传中
        path: res.tempFilePath, // 线上路径
        duration: this.duration // 录音时长
    });
    // 核对上传
    this.checkUploadVoice();
});

clearInterval(this.timerInfo);
复制代码


文件上传
  • 通过设置每个录音的状态,来记录各个状态(-1 没有上传, -100 上传失败, 100 上传成功, 0 ~ 100上传中)。
  • 上传失败可以实现重新上传。所以上传文件前,要进行核对文件(核对各个状态)。
let obj;
for (let i = 0; i < this.voiceList.length; i++) {
    let item = this.voiceList[i];
    if (item.progress == -1 || item.progress == -100) {
        obj = await this.uploadFiles(item, i); // 等待文件上传完成后,获取信息
        // 修改语音数组 通过set
        this.$set(this.voiceList, i, {
            name: item.name,
            size: item.size,
            progress: obj.progress,
            path: obj.path, //
            duration: item.duration,
            nowPlay: false,
            text: '',
            translateStatus: false, // 此处记录是否转化为文字
        });

        this.duration = 0; // 文件上传后,时间记录要清0
    }
}


// 上传文件
uploadFiles(item, i) {
    return new Promise((resolve, reject) => {
        const uploadTask = uni.uploadFile({
            url: url, // 上传图片的地址
            filePath: item.path, // 录音后拿到的地址
            name: 'file',
            header: {
                'Content-Type': 'multipart/form-data',
                accept: 'application/json'
            },
            success: upRes => {
                console.log(upRes);
                let dataInfo = JSON.parse(upRes.data);
                let { code, data } = dataInfo;
                // 成功后要返回成功的信息
                resolve({
                    path: data.fileUrl,
                    progress: 100
                });
            },
            fail: function(err) {
            	// 上传失败的时候要记录状态
                resolve({
                    path: '',
                    progress: -100
                });
            }
        });

        uploadTask.onProgressUpdate(res => {
           // 此处获取上传的进度,并且实时展示
           this.$set(this.voiceList, {
                    name: item.name,
                    size: item.size,
                    progress: res.progress,
                    path: item.path,
                    duration: item.duration
                });
            // console.log('上传进度', res.progress);
            // console.log('已经上传的数据长度', res.totalBytesSent);
            // console.log(
            //     '预期需要上传的数据总长度',
            //     res.totalBytesExpectedToSend
            // );
        });
    });
},

复制代码

多条录音播放
  • 播放的时候只保持一条正在播放(最主要的问题)
  • 停止其他正在播放的语音
  • 播放当前点击的语音
// 播放语音
async playVoice(item, index) {
    uni.showLoading({
        title: '录音播放加载中'
    });
    //  同时点击当前的语音两次,需要先把上一个停止到,再进行播放新的
    if (item.nowPlay && this.nowPlayItem.nowPlay) {
        this.stopVoice(item);
        return;
    }

    // 两条播放的语音不一样, 停止上一条,改变播放的状态,然后播放当前的
    if (!item.nowPlay && this.nowPlayItem.nowPlay) {
        let status = await this.stopVoice(item);
        let obj = Object.assign({}, this.nowPlayItem.item, {
            nowPlay: false
        });
        
        
        /**
        // 记录全局播放的音频(停止上次点击的音频,播放新的点击的音频,两次点击的音频不一样)
        nowPlayItem: {
              index: -1, // index 为当前播放的索引值
              nowPlay: false, // 当前是否有正在播放的语音
              item: null
          },
        */

        this.$set(this.voiceList, this.nowPlayItem.index, obj);
    }

    audioContext.src = http://www.wxapp-union.com/item.path; // 播放的录音地址
    audioContext.play();
    this.nowPlayItem.index = index;
    this.nowPlayItem.item = item;

    // 开始播放监听
    audioContext.onPlay(res => {
        uni.hideLoading();
        console.log('play');
        item.nowPlay = true;
        this.nowPlayItem.nowPlay = true;
    });

    // 停止播放监听(当前的播放是否停止)
    audioContext.onPause(res => {
        console.log('pause');
        item.nowPlay = false;
        this.nowPlayItem.nowPlay = false;
        // 监听音频,要和取消监听同时存在,
        audioContext.offPlay();
        audioContext.offPause();
        audioContext.offStop();
        audioContext.offEnded();
    });

    // 监听音频自然播放至结束的事件,真机调试会存在问题,微信开发者工具这块不会有问题
    audioContext.onEnded(res => {
        console.log('ended');
        item.nowPlay = false;
        this.nowPlayItem.nowPlay = false;
        // 监听音频,要和取消监听同时存在,
        audioContext.offPlay();
        audioContext.offPause();
        audioContext.offStop();
        audioContext.offEnded();
    });

    audioContext.onError(res => {
        console.log('error');
        // 播放音频失败的回调
        console.log(res);
    });
},
// 只有当前的播放停止时,才进行下一个播放开始
stopVoice(item) {
    return new Promise((resolve, reject) => {
        audioContext.stop();
        audioContext.onStop(res => {
            // 播放音频失败的回调
            this.nowPlayItem.nowPlay = false;
            if (item) {
                item.nowPlay = false;
            }
            audioContext.offPlay();
            audioContext.offPause();
            audioContext.offStop();
            audioContext.offEnded();

            resolve(true);
        });
    });
},
复制代码

此处容易出错的地方

  1. 音频监听事件要和取消事件同时存在
  2. 改变语音播放状态的时候,必须要等到上一条停止,才可以进行后续工作

总结
  1. 模拟器和真机的差别还是挺大的,真机上各个事件比模拟器会慢一点。
  2. 学海无涯,持之以恒。

相关案例查看更多