使用 Javascript AudioContext 将预加载的音频文件连接成一个文件

Concatenating preloaded audio files into one using Javascript AudioContext

我有一组预加载的 base64 编码音频文件,例如下面 javascript 对象中的两个文件(“hello”和“world”)。我希望连接两个音频并在它们之间添加 1 秒 space。我认为输出应该是一个 ArrayBuffer,或者我可以用来连接更多音频或根据需要导出/播放的东西。

注意:我希望延迟 1 秒播放它们(即我知道如何独立加载第一个文件,播放它,超时一秒钟,然后加载并播放第二个文件。我在 Just Play 的代码中尝试了这一点,但效果不佳。但这不是我想要的,因为我想构建一个组合音频文件,两个样本相隔一秒区间)。

为了完成这个答案,在提供应该连接在一起的多个短音频位(例如一个句子)的数组时,概括这个概念的最佳方法是什么?我觉得递归调用很诱人,但我不确定这对内存有什么影响?

我已经尝试了很多方法来让它发挥作用,很难说我从哪里开始以及我现在所处的位置...但我想我找到的最接近的 post 是这个:

我也有点困惑,为什么有些片段使用 UInt8Arrays 而其他片段使用 Float32s 或 Int16s...下面的代码“有效”以收听代码。在 Chrome 上,它还会为串联版本播放“hello”,但不会在 Firefox 上播放。无论哪种方式,它都不会播放“hello [1s] world”:(

  const sampleRate = 48000;
  const ctx = new (window.AudioContext || window.webkitAudioContext)( {"sampleRate": sampleRate} );

  function concatAudio( buffer1, pause, buffer2 ) {
    let silenceLength = pause*sampleRate;
    let a = new Uint8Array( buffer1.length + silenceLength + buffer2.length );
    for( let i=0; i<buffer1.length; i++ )
      a[i]=buffer1[i];
    for( let i=0; i<silenceLength; i++ )
      a[buffer1.length+i]=0;
    for( let i=0; i<buffer2.length; i++ )
      a[buffer1.length+silenceLength+i]=buffer2[i];
    return a.buffer;
  }
  
  function concatAndPlay() {
    let concatenatedBuffer = concatAudio( base64ToArrayBuffer( ogg48k.hello ), 1000, base64ToArrayBuffer( ogg48k.world ) );
    
    ctx.decodeAudioData( concatenatedBuffer ).then( function( decodedData ) {
      // following code copied from: https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode
      var source = ctx.createBufferSource();
      // set the buffer in the AudioBufferSourceNode
      source.buffer = decodedData;
      // connect the AudioBufferSourceNode to the
      // destination so we can hear the sound
      source.connect(ctx.destination);
      // start the source playing
      source.start();
    });
  }
  
  function justListen() {
    var hello = base64ToArrayBuffer( ogg48k.hello );
    console.log(hello.length); // I was hoping to use this for the sample duration, but somehow length/sampleRate is way lower than the duration...
    var d = playByteArray( hello );
    setTimeout( function(){playByteArray( base64ToArrayBuffer( ogg48k.world ) )}, 1000+800 ); // this is imperfect since I would need to retrieve the duration of the first audio, instead of manually inputing 800ms
  }

  // following code copied from: 
  function base64ToArrayBuffer(base64) {
    var binary_string = window.atob(base64);
    var len = binary_string.length;
    var bytes = new Uint8Array(len);
    for (var i = 0; i < len; i++) {
      bytes[i] = binary_string.charCodeAt(i);
    }
    return bytes;
  }

  // following code copied from: 
  function playByteArray( bytes ) {
    var buffer = new Uint8Array( bytes.length );
    buffer.set( new Uint8Array(bytes), 0 );
    ctx.decodeAudioData(buffer.buffer, play);
  }
  function play( audioBuffer ) {
    var source = ctx.createBufferSource();
    source.buffer = audioBuffer;
    source.connect( ctx.destination );
    source.start(0);
  }

  const ogg48k = {
    "hello": "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",
    "world": "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",
  };
<!DOCTYPE html>
<html>
<body>
<button onclick="concatAndPlay()">Concatenate and Play</button> (does not work)<br>
<button onclick="justListen()">just listen</button>("works", sort of, but not what I want)<br>
</body>
</html>

所以我能够从一个重复一段音频的答案中改编代码,它似乎可以工作。这是有帮助的答案: Web Audio API append/concatenate different AudioBuffers and play them as one song

下面是“解决”我的问题的一个片段,尽管我仍然有点不确定为什么它在转换为 UInt8 而不是 Int16 或 Float32 方面起作用。

如果这最终成为这里的“最佳”(或“唯一”)答案,我会接受我自己的答案来帮助其他人,但在我看来这个答案仍然不完整:

  1. 它没有提供关于如何概括“任意”长度数组的指导,比如大约 20 个小音频片段(假设总音频持续时间应保持在 3 分钟左右)。特别是在内存管理方面(例如递归是一个好的选择)?
  2. 我担心如果两个音频文件没有相同数量的声道(即一个是单声道,一个是立体声),and/or 如果它们不使用相同的采样率,可能会发生什么。 ..我会尝试通过反复试验看看在这些情况下会发生什么...

      const sampleRate = 48000;
      const ctx = new (window.AudioContext || window.webkitAudioContext)( {"sampleRate": sampleRate} );

      function concatAndPlay() {
        b1 = base64ToArrayBuffer( ogg48k.hello ).buffer;
        b2 = base64ToArrayBuffer( ogg48k.world ).buffer;
        ctx.decodeAudioData(b1, x=> ctx.decodeAudioData(b2, y=> {
          var audioSource = ctx.createBufferSource();
          audioSource.connect(ctx.destination);

          // Concatenate the two buffers into one.
          audioSource.buffer = appendBuffer(x, 1, y);
          audioSource.connect( ctx.destination );
          audioSource.start(0);
        } ) );
        
        // following code adapted from: 
        function appendBuffer(buffer1, pause, buffer2) {
          var numberOfChannels = Math.min( buffer1.numberOfChannels, buffer2.numberOfChannels );
          var tmp = ctx.createBuffer( numberOfChannels, (buffer1.length + buffer2.length + pause*buffer1.sampleRate), 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+pause*buffer1.sampleRate);
          }
          return tmp;
        }
      }
      
      function justListen() {
        var hello = base64ToArrayBuffer( ogg48k.hello );
        console.log(hello.length); // I was hoping to use this for the sample duration, but somehow length/sampleRate is way lower than the duration...
        var d = playByteArray( hello );
        setTimeout( function(){playByteArray( base64ToArrayBuffer( ogg48k.world ) )}, 1000+800 ); // this is imperfect since I would need to retrieve the duration of the first audio, instead of manually inputing 800ms
      }

      // following code copied from: 
      function base64ToArrayBuffer(base64) {
        var binary_string = window.atob(base64);
        var len = binary_string.length;
        var bytes = new Uint8Array(len);
        for (var i = 0; i < len; i++) {
          bytes[i] = binary_string.charCodeAt(i);
        }
        return bytes;
      }

      // following code copied from: 
      function playByteArray( bytes ) {
        var buffer = new Uint8Array( bytes.length );
        buffer.set( new Uint8Array(bytes), 0 );
        ctx.decodeAudioData(buffer.buffer, play);
      }
      function play( audioBuffer ) {
        var source = ctx.createBufferSource();
        source.buffer = audioBuffer;
        source.connect( ctx.destination );
        source.start(0);
      }

      const ogg48k = {
        "hello": "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",
        "world": "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",
      };
<!DOCTYPE html>
<html>
<body>
<button onclick="concatAndPlay()">Concatenate and Play</button> (now works)<br>
<button onclick="justListen()">just listen</button>("works", sort of, though I cannot get the correct spacing programatically)<br>
</body>
</html>