Record your skills, share your skills, be a great porter.

Framework to choose

The development of wechat small program, using unapp. Why do I choose this one? Because it is relatively mature. I have used MPvue before, and Kbone is particularly immature. Later, UNIAPP was selected through various types of selection.

Problems encountered

  • Recording authorization.
  • Long press the tape to see if your finger marks the long press area
  • File upload.
  • Multi-record voice playback.

Initialization work

  1. Define global recording objects and audio objects, and format your desired audio format
const recorderManager = uni.getRecorderManager(); // Create a recording object
const audioContext = wx.createInnerAudioContext(); // Create the audio object
const options = {
    duration: 600000.// Specifies the recording duration, in ms. The maximum value is 10 minutes (600000) and the default value is 1 minute (60000).
    sampleRate: 16000./ / sampling rate
    numberOfChannels: 1.// Number of recording channels
    encodeBitRate: 96000.// Code rate
    format: 'mp3'.// Audio format, valid value aAC /mp3, etc
    frameSize: 50 // Specify the frame size, in KB
};
Copy the code

The recording authorization

Recording collection is mainly considered from two aspects

  1. The user who applies for authorization directly agrees to authorize the user
  2. After the user rejects authorization, the Settings are opened and the user receives authorization.
uni.authorize({
    scope: 'scope.record'.success() {
        // The user has authorized the recording
    },
    fail() {
    	// User authorization fails. You need to open the Settings to manually authorize the user. In some cases openSetting does not open
        // The Settings can be opened by popover user via fail.
        uni.openSetting({
            success: res= > {
            	// Popup prompt without authorization
                if(! res.authSetting['scope.record']) {
                    // Recording authorization is not configured
                    uni.showModal({
                        title: 'tip'.content: 'You are not authorized to record, the function will not be used'.showCancel: false
                    });
                } else {
                    // Only the second time
                    // The user has authorized the recording}},fail: function() {
                uni.showModal({
                    title: 'tip'.content: 'You are not authorized to record, the function will not be used'.showCancel: false.success: function(res) { uni.openSetting(); }}); }}); }});Copy the code

Official API link

Interface adjustment link

Long press record/slide area/stop long press

Methods in uniapp

Longpress @longpress (finger touch exceeds 350ms) @longtap longpress @tap click @touchcancel finger touch is interrupted, such as incoming phone alert, popup window @touchend finger touch action ends, For example, release the button @touchMove after finger touch and move @touchStart finger touch action beginsCopy the code

Recording operation

  1. Gets the distance from the top of the currently clicked element
  • While holding down the press, start the timer and record the voice when authorized.
// The distance from the current clicked element is different in the applet. Here's how to get it
const query = uni.createSelectorQuery().in(this);
  query
      .select('.record-button')
      .boundingClientRect(data= > {
          // data Specifies information about the current element
      })
      .exec();
Copy the code
  1. Obtain current information through event
  • Slide out of time to stop recording, and to clear the timer.
  • Keep recording as you slide back. Keep the timer going.
  • An intermediate state is required to continue recording and stop recording by listening
let touches = e.touches[0];
  // When it is exceeded
  if (touches.pageY < this.reocrdLocation.top) {
      clearInterval(this.timerInfo);
      recorderManager.pause();
  } else {
      recorderManager.resume();
  }
Copy the code

Stop by long

  • If the recording length is too short, do not upload it.
  • If the conditions are met, upload, clear the timer, and stop recording
recorderManager.stop();
// Listen stop event
recorderManager.onStop(res= > {
    if (this.duration < 1) {
        uni.showToast({
            title: 'Recording time is too short'.duration: 1000.icon: 'none'
        });
        return;
    }
    
    // If it meets the criteria, advance the array.
   

    this.voiceList.push({
        size: res.fileSize, // Local progress
        progress: -1.//-1 does not upload, -100 fails to upload, 100 succeeds, 0-100 is uploaded
        path: res.tempFilePath, // Line path
        duration: this.duration // Recording duration
    });
    // Check the upload
    this.checkUploadVoice();
});

clearInterval(this.timerInfo);
Copy the code

File upload

  • You can set the status of each recording to record each status (-1 no upload, -100 upload failure, 100 upload success, 0-100 upload in progress).
  • If the upload fails, you can upload again. So before uploading the file, check the file (check each state).
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); // Wait until the file is uploaded to get the information
        // Modify the speech array through 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.// Whether the record is converted to text
        });

        this.duration = 0; // After the file is uploaded, the time record must be clear 0}}// Upload the file
uploadFiles(item, i) {
    return new Promise((resolve, reject) = > {
        const uploadTask = uni.uploadFile({
            url: url, // The address to upload the image
            filePath: item.path, // Get the address after recording
            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;
                Success message is returned after success
                resolve({
                    path: data.fileUrl,
                    progress: 100
                });
            },
            fail: function(err) {
            	// Upload failed to record the status
                resolve({
                    path: ' '.progress: -100}); }}); uploadTask.onProgressUpdate(res= > {
           // Get the upload progress here and display it in real time
           this.$set(this.voiceList, {
                    name: item.name,
                    size: item.size,
                    progress: res.progress,
                    path: item.path,
                    duration: item.duration
                });
            // console.log(' upload progress ', res.progress);
            // console.log(' Length of data uploaded ', res.totalBytessent);
            // console.log(
            // 'Total length of data expected to upload ',
            // res.totalBytesExpectedToSend
            // );
        });
    });
},

Copy the code

Multiple Recording Playback

  • Keep only one active while playing (main problem)
  • Stop any other voice that is playing
  • Plays the currently clicked voice
// Play voice
async playVoice(item, index) {
    uni.showLoading({
        title: 'Recording playback loading'
    });
    // Click the current voice twice at the same time, you need to stop the previous one to play the new one
    if (item.nowPlay && this.nowPlayItem.nowPlay) {
        this.stopVoice(item);
        return;
    }

    // The two voices are not the same. Stop the previous one, change the playing state, and play the current one
    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 is the index value of the current play. nowPlay: False, // Whether a voice is currently being played item: null}, */

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

    audioContext.src = item.path; // Address of the recording to be played
    audioContext.play();
    this.nowPlayItem.index = index;
    this.nowPlayItem.item = item;

    // Start listening
    audioContext.onPlay(res= > {
        uni.hideLoading();
        console.log('play');
        item.nowPlay = true;
        this.nowPlayItem.nowPlay = true;
    });

    // Stop playback listener (whether the current playback is stopped)
    audioContext.onPause(res= > {
        console.log('pause');
        item.nowPlay = false;
        this.nowPlayItem.nowPlay = false;
        // Listen on audio and cancel listen on audio
        audioContext.offPlay();
        audioContext.offPause();
        audioContext.offStop();
        audioContext.offEnded();
    });

    // Listen to the audio playback to the end of the event, real machine debugging will have a problem, wechat developer tools will not have a problem
    audioContext.onEnded(res= > {
        console.log('ended');
        item.nowPlay = false;
        this.nowPlayItem.nowPlay = false;
        // Listen on audio and cancel listen on audio
        audioContext.offPlay();
        audioContext.offPause();
        audioContext.offStop();
        audioContext.offEnded();
    });

    audioContext.onError(res= > {
        console.log('error');
        // Failed to play audio callback
        console.log(res);
    });
},
// The next playback starts only when the current playback stops
stopVoice(item) {
    return new Promise((resolve, reject) = > {
        audioContext.stop();
        audioContext.onStop(res= > {
            // Failed to play audio callback
            this.nowPlayItem.nowPlay = false;
            if (item) {
                item.nowPlay = false;
            }
            audioContext.offPlay();
            audioContext.offPause();
            audioContext.offStop();
            audioContext.offEnded();

            resolve(true);
        });
    });
},
Copy the code

This is where things go wrong

  1. The audio listen event should exist at the same time as the cancel event
  2. When changing the voice playing status, you must wait until the previous link stops before performing subsequent operations

conclusion

  1. There’s a big difference between the simulator and the real machine, where events are slower than the real one.
  2. Long is the art of learning, but persevere.

This article mainly records the problems encountered in the development, encountered what problems, what mistakes, welcome to comment on oh.