前端处理本地mp3文件,切片上传和分割播放

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <input type="file" id="ipt">
  <br>
  <br>
  <audio src="" id="audio" controls></audio>
  <br>
  <br>
  <input type="file" id="ipt2">
</body>
<script>
  const sliceSize = 1024 * 1024
  ipt.onchange = function(e) {
    const file = e.target.files[0];
    const size = file.size;

    const reader = new FileReader();
    reader.onload = function(e) {
      const res = e.target.result;
      const audioCtx = new AudioContext();
      audioCtx.decodeAudioData(res, function(audioBuffer) {
        const { newAudioBuffer, frameCount } = getNewArrayBuffer(audioBuffer, audioCtx);
        // 两种使用方式:
        // 1. 给页面元素设置src
        const blob = bufferToWave(newAudioBuffer, frameCount);
        const audio = document.querySelector('#audio');
        audio.src = window.URL.createObjectURL(blob);
        // 2. 直接播放
        // 创建AudioBufferSourceNode对象
        // var source = audioCtx.createBufferSource();
        // // 设置AudioBufferSourceNode对象的buffer为复制的3秒AudioBuffer对象
        // source.buffer = newAudioBuffer;
        // // 这一句是必须的,表示结束,没有这一句没法播放,没有声音
        // // 这里直接结束,实际上可以对结束做一些特效处理
        // source.connect(audioCtx.destination);
        // // 资源开始播放
        // source.start();
      })
    }
    reader.readAsArrayBuffer(file);
  }
  function getNewArrayBuffer(audioBuffer, ctx) {
    // 声道数量和采样率
    var channels = audioBuffer.numberOfChannels;
    var rate = audioBuffer.sampleRate;

    // 截取前5秒
    var startOffset = 0;
    var endOffset = rate * 45;
    // 5秒对应的帧数
    var frameCount = endOffset - startOffset;
    // 创建同样采用率、同样声道数量,长度是前3秒的空的AudioBuffer
    var newAudioBuffer = new AudioContext().createBuffer(channels, endOffset - startOffset, rate);
    // 创建临时的Array存放复制的buffer数据
    var anotherArray = new Float32Array(frameCount);
    // 声道的数据的复制和写入
    var offset = 0;
    for (var channel = 0; channel < channels; channel++) {
      audioBuffer.copyFromChannel(anotherArray, channel, startOffset);
      newAudioBuffer.copyToChannel(anotherArray, channel, offset);
    }
    // newAudioBuffer就是全新的复制的5秒长度的AudioBuffer对象
    return {
      newAudioBuffer,
      frameCount
    };
  }

  function bufferToWave(abuffer, len) {
    var numOfChan = abuffer.numberOfChannels,
    length = len * numOfChan * 2 + 44,
    buffer = new ArrayBuffer(length),
    view = new DataView(buffer),
    channels = [], i, sample,
    offset = 0,
    pos = 0;

    // write WAVE header
    // "RIFF"
    setUint32(0x46464952);
    // file length - 8                      
    setUint32(length - 8);
    // "WAVE"                     
    setUint32(0x45564157);
    // "fmt " chunk
    setUint32(0x20746d66);  
    // length = 16                       
    setUint32(16);  
    // PCM (uncompressed)                               
    setUint16(1); 
    setUint16(numOfChan);
    setUint32(abuffer.sampleRate);
    // avg. bytes/sec
    setUint32(abuffer.sampleRate * 2 * numOfChan);
    // block-align
    setUint16(numOfChan * 2);
    // 16-bit (hardcoded in this demo)
    setUint16(16);                           
    // "data" - chunk
    setUint32(0x61746164); 
    // chunk length                   
    setUint32(length - pos - 4);                   

    // write interleaved data
    for(i = 0; i < abuffer.numberOfChannels; i++)
      channels.push(abuffer.getChannelData(i));
      while(pos < length) {
        // interleave channels
        for(i = 0; i < numOfChan; i++) {
          // clamp
          sample = Math.max(-1, Math.min(1, channels[i][offset])); 
          // scale to 16-bit signed int
          sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767)|0; 
          // write 16-bit sample
          view.setInt16(pos, sample, true);          
          pos += 2;
        }
        // next source sample
        offset++                                     
      }
      // create Blob
    return new Blob([buffer], {type: "audio/wav"});
    function setUint16(data) {
      view.setUint16(pos, data, true);
      pos += 2;
    }
    function setUint32(data) {
      view.setUint32(pos, data, true);
      pos += 4;
    }
  }
  
  // 也可使用file的slice对文件进行切片
  const ipt2 = document.querySelector('#ipt2');
  ipt2.onchange = function(e) {
    const file = e.target.files[0];
    const size = file.size;
    console.log(size)
    const list = [];
    let n = 0;
    let cur = 0;
    while (n < size) {
      list.push({
        file: file.slice(n, n + sliceSize),
        i: cur
      })
      n += sliceSize;
      cur++
    }
    console.log(list)
    const reader = new FileReader();
    reader.readAsArrayBuffer(list[0].file);
    reader.onload = function(e) {
      const res = e.target.result;
      const ctx = new AudioContext();
      console.log(res, ctx);
      var source = ctx.createBufferSource();
      // 设置AudioBufferSourceNode对象的buffer为复制的3秒AudioBuffer对象
      ctx.decodeAudioData(res, function(ab) {
        console.log(ab, 'ab')
        source.buffer = ab;
        source.connect(ctx.destination);
        source.start();
      })
    }
    console.log(list, 'file-list')
  }
</script>
</html>
posted @ 2022-08-12 16:18  ANEANE  阅读(679)  评论(0编辑  收藏  举报