【问题标题】:Web Audio API append/concatenate different AudioBuffers and play them as one songWeb Audio API 附加/连接不同的 AudioBuffers 并将它们作为一首歌曲播放
【发布时间】:2012-12-18 02:17:13
【问题描述】:

我一直在使用 Web Audio API,我正在尝试加载歌曲的多个部分并将它们附加到新的 ArrayBuffer 中,然后使用该 ArrayBuffer 将所有部分作为一首歌曲播放。在以下示例中,我使用相同的歌曲数据(这是一个小循环)而不是歌曲的不同部分。

问题是它仍然只播放一次而不是两次,我不知道为什么。

Download song

function init() {

  /**
   * Appends two ArrayBuffers into a new one.
   * 
   * @param {ArrayBuffer} buffer1 The first buffer.
   * @param {ArrayBuffer} buffer2 The second buffer.
   */
  function appendBuffer(buffer1, buffer2) {
    var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
    tmp.set( new Uint8Array(buffer1), 0);
    tmp.set( new Uint8Array(buffer2), buffer1.byteLength);
    return tmp;
  }

  /**
   * Loads a song
   * 
   * @param {String} url The url of the song.
   */
  function loadSongWebAudioAPI(url) {
    var request = new XMLHttpRequest();
    var context = new webkitAudioContext();

    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    /**
     * Appends two ArrayBuffers into a new one.
     * 
     * @param {ArrayBuffer} data The ArrayBuffer that was loaded.
     */
    function play(data) {
      // Concatenate the two buffers into one.
      var a = appendBuffer(data, data);
      var buffer = a.buffer;
      var audioSource = context.createBufferSource();
      audioSource.connect(context.destination);

      //decode the loaded data
      context.decodeAudioData(buffer, function(buf) {
        console.log('The buffer', buf);
        audioSource.buffer = buf;
        audioSource.noteOn(0);
        audioSource.playbackRate.value = 1;
      });

    };

    // When the song is loaded asynchronously try to play it.
    request.onload = function() {
      play(request.response);
    }

    request.send();
  }


  loadSongWebAudioAPI('http://localhost:8282/loop.mp3');
}

window.addEventListener('load',init,false);

【问题讨论】:

    标签: javascript concatenation html5-audio web-audio-api arraybuffer


    【解决方案1】:

    您的代码中的问题是您正在复制 MP3 文件的另一个副本并将其附加到其自身的末尾。该副本被解码器有效地忽略了 - 它不是原始缓冲区数据,它只是文件流中的随机虚假垃圾,跟随一个完美完整的 MP3 文件。

    您需要做的是首先将音频数据解码到 AudioBuffer 中,然后将音频缓冲区一起附加到新的 AudioBuffer 中。这需要对您的代码进行一些重组。

    你想做的是这样的:

    var context = new webkitAudioContext();
    
    function init() {
    
      /**
       * Appends two ArrayBuffers into a new one.
       * 
       * @param {ArrayBuffer} buffer1 The first buffer.
       * @param {ArrayBuffer} buffer2 The second buffer.
       */
      function appendBuffer(buffer1, buffer2) {
        var numberOfChannels = Math.min( buffer1.numberOfChannels, buffer2.numberOfChannels );
        var tmp = context.createBuffer( numberOfChannels, (buffer1.length + buffer2.length), buffer1.sampleRate );
        for (var i=0; i<numberOfChannels; i++) {
          var channel = tmp.getChannelData(i);
          channel.set( buffer1.getChannelData(i), 0);
          channel.set( buffer2.getChannelData(i), buffer1.length);
        }
        return tmp;
      }
    
      /**
       * Loads a song
       * 
       * @param {String} url The url of the song.
       */
      function loadSongWebAudioAPI(url) {
        var request = new XMLHttpRequest();
    
        request.open('GET', url, true);
        request.responseType = 'arraybuffer';
    
        /**
         * Appends two ArrayBuffers into a new one.
         * 
         * @param {ArrayBuffer} data The ArrayBuffer that was loaded.
         */
        function play(data) {
          //decode the loaded data
          context.decodeAudioData(data, function(buf) {
            var audioSource = context.createBufferSource();
            audioSource.connect(context.destination);
    
            // Concatenate the two buffers into one.
            audioSource.buffer = appendBuffer(buf, buf);
            audioSource.noteOn(0);
            audioSource.playbackRate.value = 1;
          });
    
        };
    
        // When the song is loaded asynchronously try to play it.
        request.onload = function() {
          play(request.response);
        }
    
        request.send();
      }
    
    
      loadSongWebAudioAPI('loop.mp3');
    }
    
    window.addEventListener('load',init,false);
    

    有轻微的播放间隙 - 这是因为您的声音样本开始时有近 50 毫秒的静音,而不是由于循环问题。

    希望这会有所帮助!

    【讨论】:

    • 谢谢,您的评论帮助了我! 72lions.github.com/PlayingChunkedMP3-WebAudioAPI
    • @cwilso 是否可以在不同的播放时间将多个 AudioBufferSourceNode 连接成一个?
    • 好吧,您可以将它们连接到同一个目的地。我认为这具有您正在寻找的效果。
    • @cwilso 感谢您的 appendBuffer() 示例!只是一个简单的问题:为什么使用 Math.min() 来获得最低的 buffer.numberOfChannels?用最高的怎么样?会错吗?
    • @faks 如果您使用 Math.max,您将尝试从不存在的缓冲区或不存在的 TO 缓冲区复制。此算法适用于立体声到立体声,但也只会复制两个通道,如果一个是立体声,一个是四通道,例如(如果参数大于缓冲区中的通道数,则 buffer.getchanneldata/setchanneldata 将失败。)
    【解决方案2】:

    如果您需要从数组(不仅是 2 个)追加/连接缓冲区列表,这里有一个解决方案。我刚刚稍微调整了@Cwilso 代码(感谢您的帮助;)

    function concatBuffer(_buffers) {
        // _buffers[] is an array containig our audiobuffer list
    
        var buflengh = _buffers.length;
        var channels = [];
        var totalDuration = 0;
    
        for(var a=0; a<buflengh; a++){
            channels.push(_buffers[a].numberOfChannels);// Store all number of channels to choose the lowest one after
            totalDuration += _buffers[a].duration;// Get the total duration of the new buffer when every buffer will be added/concatenated
        }
    
        var numberOfChannels = channels.reduce(function(a, b) { return Math.min(a, b); });;// The lowest value contained in the array channels
        var tmp = context.createBuffer(numberOfChannels, context.sampleRate * totalDuration, context.sampleRate);// Create new buffer
    
        for (var b=0; b<numberOfChannels; b++) {
            var channel = tmp.getChannelData(b);
            var dataIndex = 0;
    
            for(var c = 0; c < buflengh; c++) {
                channel.set(_buffers[c].getChannelData(b), dataIndex);
                dataIndex+=_buffers[c].length;// Next position where we should store the next buffer values
            }
        }
        return tmp;
    }
    

    【讨论】:

    • 而不是使用totalDuration,它应该计算totalSamples,否则它会因为浮点错误而崩溃...:var totalSamples = 0; for(var a=0; a
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多